﻿namespace GreenIsomer.Web.Controls.JQuery {
	using System;
	using System.ComponentModel;
	using System.Globalization;

	/// <summary>
	/// Defines an aspect ratio.
	/// </summary>
	[TypeConverter(typeof(AspectRatioConverter))]
	public struct AspectRatio : IEquatable<AspectRatio>, IComplexProperty {


		#region Static Readonly Fields: Standard values

		/// <summary>
		/// Instructs jQuery UI not to constrain the aspect ratio.
		/// </summary>
		public static readonly AspectRatio Unconstrained = new AspectRatio();

		/// <summary>
		/// Instructs jQuery UI to constrain using the existing aspect ratio.
		/// </summary>
		public static readonly AspectRatio Current = new AspectRatio(true);

		/// <summary>
		/// Instructs jQuery UI to constrain as a square.
		/// </summary>
		public static readonly AspectRatio Square = new AspectRatio(1F);

		#endregion


		#region Members

		private readonly float ratio;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new GreenIsomer.Web.Controls.JQuery.AspectRatio value using specified behavior.
		/// </summary>
		/// <param name="useCurrent">A value of true will constrain using the current aspect ratio while false will not constrain.</param>
		public AspectRatio(bool useCurrent) {
			ratio = useCurrent ? float.NaN : 0F;
		}

		/// <summary>
		/// Initializes a new GreenIsomer.Web.Controls.JQuery.AspectRatio value using a specific value.
		/// </summary>
		/// <param name="ratio">The aspect ratio used to constrain.</param>
		/// <exception cref="ArgumentOutOfRangeException">Ratio value must be a non-negative real number.</exception>
		public AspectRatio(float ratio) {
			if(ratio <= 0 || float.IsInfinity(ratio) || float.IsNaN(ratio))
				throw new ArgumentOutOfRangeException("ratio", "Value must be a non-negative real number.");

			this.ratio = ratio;
		}

		/// <summary>
		/// Initializes a new GreenIsomer.Web.Controls.JQuery.AspectRatio value using a specific value.
		/// </summary>
		/// <param name="width">The width portion of the ratio used to constrain.</param>
		/// <param name="height">The height portion of the ratio used to constrain.</param>
		public AspectRatio(int width, int height) : this((float)width / (float)height) { }

		#endregion


		#region Properties

		/// <summary>
		/// If applicable, the specific ratio used to constrain.
		/// </summary>
		public float? Ratio {
			get { return ratio == 0F || float.IsNaN(ratio) ? (float?)null : ratio; }
		}

		/// <summary>
		/// Whether or not the current aspect ratio should be used to constrain.
		/// </summary>
		public bool UseCurrent {
			get { return float.IsNaN(ratio); }
		}

		/// <summary>
		/// Whether or not the aspect ratio is constrained.
		/// </summary>
		public bool IsConstrained {
			get { return ratio != 0F; }
		}

		#endregion


		#region Methods: Object management

		/// <summary>
		/// Determines whether or not the specified object is equal to the current GreenIsomer.Web.Controls.JQuery.AspectRatio value.
		/// </summary>
		/// <param name="obj">The System.Object to compare.</param>
		/// <returns>Whether or not the provided object is equal.</returns>
		public override bool Equals(object obj) {
			if(obj is AspectRatio) return this == (AspectRatio)obj;
			return false;
		}

		/// <summary>
		/// Determines whether or not the specified AspectRatio is equal to the current GreenIsomer.Web.Controls.JQuery.AspectRatio value.
		/// </summary>
		/// <param name="other">The GreenIsomer.Web.Controls.JQuery.AspectRatio to compare.</param>
		/// <returns>Whether or not the provided AspectRatio value is equal.</returns>
		public bool Equals(AspectRatio other) {
			return this == other;
		}

		/// <summary>
		/// Serves as a hash function for GreenIsomer.Web.Controls.JQuery.AspectRatio.
		/// </summary>
		/// <returns>The hash value associated with the contained data.</returns>
		public override int GetHashCode() {
			return ratio.GetHashCode();
		}

		/// <summary>
		/// Returns a System.String that represents the current GreenIsomer.Web.Controls.JQuery.AspectRatio value.
		/// </summary>
		/// <returns>The string representation.</returns>
		public override string ToString() {
			if(Ratio.HasValue) return ratio.ToString(CultureInfo.InvariantCulture);
			return IsConstrained.ToString(CultureInfo.InvariantCulture);
		}

		#endregion


		#region Operators

		/// <summary>
		/// Compares the equality of two GreenIsomer.Web.Controls.JQuery.AspectRatio values.
		/// </summary>
		/// <param name="value0">The first AspectRatio value.</param>
		/// <param name="value1">The second AspectRatio value.</param>
		/// <returns>Whether or not the two AspectRatio values are equal.</returns>
		public static bool operator ==(AspectRatio value0, AspectRatio value1) {
			return value0.ratio == value1.ratio;
		}

		/// <summary>
		/// Compares the inequality of two GreenIsomer.Web.Controls.JQuery.Duration values.
		/// </summary>
		/// <param name="value0">The first AspectRatio value.</param>
		/// <param name="value1">The second AspectRatio value.</param>
		/// <returns>Whether or not the two AspectRatio values are distinct.</returns>
		public static bool operator !=(AspectRatio value0, AspectRatio value1) {
			return value0.ratio != value1.ratio;
		}

		#endregion


		#region Explicit implementation: IPropertyValue

		void IComplexProperty.AddPropertyValue(PropertyCollection properties, string name) {
			if(!IsConstrained) properties.AddValue(name, false);
			else if(UseCurrent) properties.AddValue(name, true);
			else properties.AddValue(name, ratio);
		}

		#endregion


	}

}